Passed
Push — feature/typescript5 ( 73fa6e...5465cb )
by Lorenzo
01:55
created

ExpressBeans.initialize   B

Complexity

Conditions 5

Size

Total Lines 36
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 5

Importance

Changes 0
Metric Value
eloc 20
dl 0
loc 36
c 0
b 0
f 0
ccs 15
cts 15
cp 1
rs 8.9332
cc 5
crap 5
1
import express, { Express } from 'express';
2
import { CreateExpressBeansOptions, ExpressBeansOptions, ExpressRouterBean } from '@/ExpressBeansTypes';
3
import { logger, registeredBeans } from '@/decorators';
4
5
export default class ExpressBeans {
6
  private readonly app: Express;
7
8
  private onInitialized: (() => void) | undefined;
9
10
  /**
11
   * Creates a new ExpressBeans application
12
   * @param options {ExpressBeansOptions}
13
   */
14
  static createApp(options?: Partial<CreateExpressBeansOptions>): Promise<ExpressBeans> {
15 4
    return new Promise((resolve, reject) => {
16
      let app: ExpressBeans;
17 4
      const onInitialized = () => {
18 1
        resolve(app);
19
      };
20 4
      app = new ExpressBeans({ ...options, onInitialized, onError: reject });
21
    });
22
  }
23
24
  /**
25
   * Constructor of ExpressBeans application
26
   * @constructor
27
   * @param options {ExpressBeansOptions}
28
   */
29
  constructor(options?: Partial<ExpressBeansOptions>) {
30 15
    this.app = express();
31 15
    this.app.disable('x-powered-by');
32 15
    this.initialize(options || {});
33
  }
34
35
  /**
36
   * Initializes the application and checks
37
   * if all beans are valid
38
   * @param listen {boolean}
39
   * @param port {number}
40
   * @param beans {Object[]}
41
   * @param onInitialized {Function}
42
   * @private
43
   */
44
  private initialize({
45
    listen = true,
46
    port = 8080,
47
    routerBeans = [],
48
    onInitialized,
49
    onError,
50
  }: Partial<ExpressBeansOptions>) {
51 15
    this.onInitialized = onInitialized;
52 15
    const invalidBeans = routerBeans
53 13
      .filter(((bean) => !bean.isExpressBean))
54 1
      .map((object) => object.prototype.constructor.name);
55 15
    if (invalidBeans.length > 0) {
56 1
      throw new Error(`Trying to use something that is not an ExpressBean: ${invalidBeans.join(', ')}`);
57
    }
58 14
    setImmediate(async () => {
59 13
      try {
60 13
        this.registerRouters();
61 12
        if (listen) {
62 10
          this.listen(port);
63
        }
64
      } catch (err: any) {
65 4
        if (onError) {
66 3
          onError(err);
67
        } else {
68 1
          logger.error(new Error('Critical error', { cause: err }));
69 1
          process.exit(1);
70
        }
71
      }
72
    });
73
  }
74
75
  /**
76
   * Starts the server and calls onInitialized callback
77
   * @param {number} port
78
   */
79
  listen(port: number) {
80 11
    return this.app.listen(port, () => {
81 4
      logger.info(`Server listening on port ${port}`);
82 4
      if (this.onInitialized) {
83 2
        this.onInitialized();
84
      }
85
    });
86
  }
87
88
  private registerRouters() {
89 13
    Array.from(registeredBeans.values())
90 8
      .map((bean) => bean as ExpressRouterBean)
91 8
      .filter((bean) => bean.routerConfig)
92
      .forEach((bean) => {
93 8
        try {
94
          const {
95
            path,
96
            router,
97 8
          } = bean.routerConfig;
98 8
          logger.debug(`Registering router ${bean.className}`);
99 8
          this.app.use(path, router);
100
        } catch (e) {
101 1
          logger.error(e);
102 1
          throw new Error(`Router ${bean.className} not initialized correctly`);
103
        }
104
      });
105
  }
106
107
  /**
108
   * Gets Express application
109
   * @returns {Express}
110
   */
111
  getApp() {
112 2
    return this.app;
113
  }
114
115
  /**
116
   * Exposes use function of Express application
117
   * @param handlers
118
   */
119
  use(...handlers: any) {
120 1
    this.app.use(...handlers);
121
  }
122
}
123